package com.sti.framework.core.annotation.authorization;


import com.sti.framework.core.data.IDEntity;
import com.sti.framework.core.redis.RedisUtil;
import com.sti.framework.core.security.jwt.JwtException;
import com.sti.framework.core.security.jwt.JwtUtil;
import com.sti.framework.core.web.ResponseBodyEntity;
import com.sti.framework.models.User.data.Logs;
import com.sti.framework.models.User.data.RoleAuthority;
import com.sti.framework.models.User.service.LogsService;
import com.sti.framework.models.User.service.RoleAuthorityService;
import com.sti.framework.models.User.service.UserService;
import io.jsonwebtoken.Claims;
import io.swagger.annotations.Api;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;


/**
 * 权限检查
 * 1、启动时自动同步所有需要检查权限（设置）的接口
 * 2、访问接口时检查是否有权限
 * 3、记录接口访问日志
 */
@Aspect
@Component
public class AuthorizationHandler {


    @Value("${spring.SingleClientVerification}")
    private boolean singleClientVerification;

    @Inject
    private LogsService logsService;

    @Inject
    private UserService userService;

    @Inject
    private RoleAuthorityService roleAuthorityService;

    @Resource
    private RedisUtil redisUtil;


    @Value(value = "${spring.datasource.url}")
    private String datasource;
    @Value(value = "${spring.datasource.username}")
    private String username;
    @Value(value = "${spring.datasource.password}")
    private String password;
    @Value(value = "${spring.SyncAuthor}")
    private boolean syncAuthor;

    private Connection conn;

    @Around("@annotation(authorizations)")
    public Object around(ProceedingJoinPoint proceedingJoinPoint, Authorizations authorizations) throws Throwable {
        Signature signature = proceedingJoinPoint.getSignature();
        if (!(signature instanceof MethodSignature)) {
            throw new IllegalArgumentException("the Annotation @Authorization must used on method!");
        }

        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = (HttpServletRequest) ((ServletRequestAttributes) requestAttributes).getRequest();

        Class<?> cls = proceedingJoinPoint.getTarget().getClass();
        for (int i = 0; i < authorizations.value().length; i++) {
            if (checkAuthorization(proceedingJoinPoint, request, authorizations.value()[i],
                    cls.getAnnotation(RequestMapping.class).value()[0],
                    cls.getAnnotation(Api.class).tags()[0]
            )) {
                return proceedingJoinPoint.proceed();
            }
        }
        throw new JwtException(ResponseBodyEntity.ResponseStatus.FORBIDDEN);
    }

    @Around("@annotation(authorization)")
    public Object around(ProceedingJoinPoint proceedingJoinPoint, Authorization authorization) throws Throwable {
        Signature signature = proceedingJoinPoint.getSignature();
        if (!(signature instanceof MethodSignature)) {
            throw new IllegalArgumentException("the Annotation @Authorization must used on method!");
        }

        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = (HttpServletRequest) ((ServletRequestAttributes) requestAttributes).getRequest();

        Class<?> cls = proceedingJoinPoint.getTarget().getClass();
        if (checkAuthorization(proceedingJoinPoint, request, authorization,
                cls.getAnnotation(RequestMapping.class).value()[0],
                cls.getAnnotation(Api.class).tags()[0]
        )) {
            return proceedingJoinPoint.proceed();
        }
        throw new JwtException(ResponseBodyEntity.ResponseStatus.FORBIDDEN, authorization.describes());
    }

    /**
     * 检查权限
     *
     * @param proceedingJoinPoint
     * @param request
     * @param authorization
     * @param controllerUrl
     * @param tableName
     * @return
     */
    private boolean checkAuthorization(ProceedingJoinPoint proceedingJoinPoint, HttpServletRequest request, Authorization authorization, String controllerUrl, String tableName) {
        String token = request.getHeader("token");
        if(StringUtils.isNotEmpty(token)){
            try {
                Claims claims = JwtUtil.checkJWT(token);
                Long role_id = (Long)claims.get("role_id", Long.class);
                Long user_id = (Long)claims.get("user_id", Long.class);
                String username = (String)claims.get("username", String.class);
                request.setAttribute("user_id", user_id);
                request.setAttribute("role_id", role_id);
                request.setAttribute("username", username);
            } catch (JwtException var13) {
                throw new JwtException(ResponseBodyEntity.ResponseStatus.TOKENEXPIRED);
            }
        }
        String tag = authorization.tag().indexOf("/Api") == 0 ? authorization.tag() : controllerUrl + authorization.tag();
        Long role_id = (Long) request.getAttribute("role_id");
        Long user_id = (Long) request.getAttribute("user_id");
        String username = (String) request.getAttribute("username");
        if (role_id == null || user_id == null || username == null) {
            throw new JwtException(ResponseBodyEntity.ResponseStatus.FORBIDDEN, authorization.describes());
        } else {

            // 检查redis
            if (singleClientVerification) {
                String redisToken = (String) redisUtil.get("Session_" + user_id);
                String sSessionWeChat = (String) redisUtil.get("Session_weChat_" + user_id);
                String sessionApp = (String) redisUtil.get("Session_App_" + user_id);
                int isNotTokenCount = 0;
                if (StringUtils.isEmpty(redisToken) || !redisToken.equals(request.getHeader("token"))) {
                    isNotTokenCount+=1;
                }
                if (StringUtils.isEmpty(sSessionWeChat) || !sSessionWeChat.equals(request.getHeader("token"))) {
                    isNotTokenCount+=1;
                }
                if (StringUtils.isEmpty(sessionApp) || !sessionApp.equals(request.getHeader("token"))) {
                    isNotTokenCount+=1;
                }
                if(isNotTokenCount == 2){
                    throw new JwtException(ResponseBodyEntity.ResponseStatus.TOKENEXPIREDCLIENT);
                }
            }

            // 如果角色id=1不做限制
            if (role_id.equals(23691354597501L) || role_id.equals(23691354597502L)) {
                // 记录接口调用日志
                insertLogs(username, user_id, tag, tableName, authorization.describes(), request.getQueryString(), request.getAttribute("_body"), request);
                return true;
            }
            // 查询是否有权限
            RoleAuthority roleAuthority = roleAuthorityService.getForRole(role_id, tag);
            if (roleAuthority == null) {
                return false;
            } else {
                // 禁止访问
                if (roleAuthority.getAccess_level() == 2) {
                    return false;
                }
                // 只能操作自己的数据
                if (roleAuthority.getData_level() == 2) {

                    Boolean checkValid = authorization.checkValid();
                    // 不检查权限
                    if (checkValid == false) {
                        // 记录接口调用日志
                        insertLogs(username, user_id, tag, tableName, authorization.describes(), request.getQueryString(), request.getAttribute("_body"), request);
                        return true;
                    }
                    if (request.getMethod().equals("POST")) {
                        String fakeid = request.getParameter("fakeId");
                        if (fakeid == null || fakeid.isEmpty()) {
                            throw new JwtException(ResponseBodyEntity.ResponseStatus.FORBIDDEN_34036);
                        }
                        String _fakeid = request.getParameter("id") + request.getAttribute("user_id");
                        if (!fakeid.equals(DigestUtils.md5DigestAsHex(_fakeid.getBytes()))) {
                            throw new JwtException(ResponseBodyEntity.ResponseStatus.FORBIDDEN);
                        }

                    } else if (request.getMethod().equals("GET")) {
                        request.setAttribute("creatorid", user_id);
                    }
                }
                // 记录登陆日志
                insertLogs(username, user_id, tag, tableName, authorization.describes(), request.getQueryString(), request.getAttribute("_body"), request);
                return true;
            }

        }
    }

    /**
     * 同步所有接口信息
     *
     * @param authorization
     * @param cls
     */
    private void importAuthData(Authorization authorization, Class<?> cls) {
        if (authorization.impor().equals("true")) {
            Api api = cls.getAnnotation(Api.class);
            RequestMapping requestMapping = cls.getAnnotation(RequestMapping.class);

            String tag = authorization.tag();
            if (!tag.isEmpty()) {
                tag = requestMapping.value()[0] + authorization.tag();
            } else {
                tag = authorization.value();
            }

            try {
                PreparedStatement preparedStatement = conn.prepareStatement("SELECT * FROM user_authority WHERE tag = \'" + tag + "\';");
                ResultSet resultSet = preparedStatement.executeQuery();

                if (!resultSet.next()) {
                    conn.prepareStatement("INSERT INTO user_authority (id, tag, describes, table_name, model_name, hide, FAKEID, CREATED_DATE, CREATOR_ID, LAST_MODIFIED_DATE, DELETED) VALUES (" + IDEntity.newId() + ", '" + tag + "', '" + authorization.describes() + "', '" + api.tags()[0] + "', '" + api.value() + "', " + Boolean.valueOf(authorization.hide()) + ", NULL, NULL, NULL, NULL, NULL);").execute();
                }
                resultSet.close();

            } catch (Exception e) {
                System.out.println("权限信息初始化失败!");
                System.out.println(e.getMessage());
            }
        }
    }

    public void insertLogs(String username, Long user_id, String tag, String tableName, String action, String queryString, Object body, HttpServletRequest request) {

        // 记录登陆日志
        Logs log = new Logs();
        log.setUsername(username);
        log.setUserid(Long.valueOf(user_id));
        log.setTable_name(tableName);
        log.setTag(tag);
        log.setAction(action);
        log.setQueryString(queryString);
        if (body != null) {
            log.setBody(body.toString());
        }

        logsService.create(log);
    }
}
